Skip to content

Enforce coarse application network policy - #14

Open
omry wants to merge 1 commit into
pr13from
pr14
Open

Enforce coarse application network policy#14
omry wants to merge 1 commit into
pr13from
pr14

Conversation

@omry

@omry omry commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Add portable public and local runtime-network controls that default to deny, compile into the locked runtime policy, and grant declared inbound ports only to the persistent workload shape.

Install IPv4 and IPv6 nftables policy through the trusted startup helper, then irreversibly drop setup capabilities and assume the final application identity for workloads, commands, shells, lifecycle actions, and private-environment execution. Preserve declared endpoint traffic, constrain Docker DNS, and admit only established responses plus related ICMP network errors.

Add unit and live Docker coverage for all policy combinations, root and non-root execution, DNS, endpoint persistence, and transient isolation. Document the security boundary and deferred gateway design, update the backlog, and add a security changelog fragment.


Stack created with Sapling. Best reviewed with ReviewStack.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements a default-deny coarse application network policy (public vs local) as part of the locked runtime policy, enforced via a trusted in-container startup helper that installs nftables rules (IPv4/IPv6), drops capabilities/securebits, verifies /proc/self/status, and then execs the application. This extends the blueprint environment model with portable runtime.network controls, ensures declared inbound workload ports are preserved only for the persistent workload plan shape, and adds unit + Docker integration coverage plus documentation and a security changelog fragment.

Changes:

  • Add environment.runtime.network.{public,local} (default deny) to blueprint syntax/model/resolve, and carry it through Docker planning and runtime policy compilation/digesting.
  • Introduce sandbox-exec / restricted-exec probe contracts, nftables firewall installation, and stricter kernel-status verification (including inheritable/ambient caps).
  • Add extensive unit/integration tests, update docs/backlog, and add a .changes security fragment.

Reviewed changes

Copilot reviewed 49 out of 50 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
internal/probe/startup_verifier.go Expand kernel-status verification to include CapInh/CapAmb.
internal/probe/startup_verifier_test.go Update verifier test fixture and fail-closed cases for new fields.
internal/probe/sandbox_exec.go Add sandbox-exec/restricted-exec argument parsing for identity/network policy.
internal/probe/sandbox_exec_test.go Unit tests for sandbox-exec parsing and restricted-exec argument rejection.
internal/probe/sandbox_exec_other.go Non-Linux stub for sandbox setup.
internal/probe/sandbox_exec_linux.go Linux sandbox setup: install firewall, drop authority, verify+exec.
internal/probe/network_firewall_linux.go Implement nftables-based coarse public/local policy + inbound TCP allowances.
internal/probe/network_firewall_linux_test.go Unit tests for conntrack mask, ICMP related types, and CIDR classification.
internal/probe/main.go Add top-level dispatch for sandbox-exec/restricted-exec modes.
internal/dockerdeploy/testdata/resolved_compose.yaml Update golden compose to run probe as root + capabilities and call sandbox-exec wrapper.
internal/dockerdeploy/testdata/network_policy_helper/main.go Integration helper binary for validating isolation, DNS behavior, and kernel status in containers.
internal/dockerdeploy/runtime_readiness_test.go Treat changed locked network policy as stale.
internal/dockerdeploy/runtime_policy_compile.go Canonicalize/validate inbound TCP grants and record effective runtime network in policy.
internal/dockerdeploy/runtime_policy_compile_test.go Add tests for network persistence and inbound TCP grant enforcement by plan shape.
internal/dockerdeploy/runtime_plan.go Verify sandbox network matches blueprint and lock inbound TCP only to workload plan.
internal/dockerdeploy/runtime_plan_test.go Add tests for network mismatch and workload-only inbound grants.
internal/dockerdeploy/runtime_host_preflight_test.go Update runtime policy fixtures to include network + inbound-tcp arrays.
internal/dockerdeploy/provider_install_host_execute.go Thread sandbox plan through provider install host execution path.
internal/dockerdeploy/provider_graph_validation_test.go Update runtime policy fixture to include network.
internal/dockerdeploy/private_workload_environment_test.go Route private-env exec through restricted-exec wrapper and pass sandbox plan through inject APIs.
internal/dockerdeploy/private_workload_environment_integration_test.go Include sandbox plan when injecting private environment in integration path.
internal/dockerdeploy/private_workload_environment_inject.go Wrap docker exec relay via probe restricted-exec contract.
internal/dockerdeploy/prepared_python_graph_reuse_test.go Update runtime policy fixtures to include network.
internal/dockerdeploy/installed_service_container.go Thread sandbox plan through installed-service container execution.
internal/dockerdeploy/full_validation_test.go Update runtime policy fixture to include network.
internal/dockerdeploy/execution_render.go Render workload container as root + minimal caps, run probe entrypoint with sandbox-exec args, add inbound-tcp grants.
internal/dockerdeploy/execution_render_test.go Update compose assertions for root+cap_add and sandbox-exec args.
internal/dockerdeploy/execution_plan.go Build sandbox plan with blueprint runtime.network normalization.
internal/dockerdeploy/current_workload_lifecycle.go Pass sandbox plan into private-environment inject path.
internal/dockerdeploy/current_workload_lifecycle_test.go Update lifecycle backend signature for inject to include sandbox plan.
internal/dockerdeploy/command_execution.go Run transient containers via probe sandbox-exec under root+minimal caps.
internal/dockerdeploy/command_execution_test.go Update transient/shell command assertions to match sandbox-exec wrapper.
internal/dockerdeploy/build_publication_test.go Update runtime policy fixture to include network.
internal/dockerdeploy/application_sandbox_plan.go Add sandbox Network policy + normalization and validation (allow/deny only).
internal/dockerdeploy/application_sandbox_plan_test.go Test network-to-capability mapping, non-workload behavior, and inbound grant isolation.
internal/dockerdeploy/application_network_policy_integration_test.go New Docker integration test matrix for public/local combinations + persistence + DNS behavior.
internal/deploy/runtime_verifier.go Update startup verifier recipe identifier for new sandbox-exec/network-policy contract.
internal/deploy/runtime_policy.go Extend runtime policy schema with Network and per-plan InboundTCP; validate canonical ports and access values.
internal/deploy/runtime_policy_test.go Update valid fixture + add digest sensitivity and validation cases for network/inbound-tcp.
internal/blueprint/syntax.go Add YAML syntax for environment.runtime.network.{public,local}.
internal/blueprint/resolve.go Resolve/validate runtime network access with allow/deny + default deny.
internal/blueprint/resolve_test.go Add tests for runtime network defaulting and validation errors.
internal/blueprint/model.go Add typed NetworkAccess + RuntimeNetwork to the environment model.
go.sum Add nftables/netlink/socket dependencies for firewall implementation.
go.mod Add github.com/google/nftables and related indirect deps.
docs/CONTROLLED_SESSION_DESIGN.md Document sandbox-exec/nftables enforcement boundary and deferred gateway scope.
docs/BLUEPRINT_ENVIRONMENT_MODEL.md Document new runtime.network public/local semantics and Linux/Docker realization.
docs/BACKLOG.md Remove the coarse network policy item from “Now” backlog (implemented).
docs/APT_PROVIDER_DETAIL_DESIGN.md Update runtime policy design to include Network + InboundTCP and new startup helper behavior.
.changes/unreleased/+coarse-application-network-policy.yaml Add Security changelog fragment for default-deny networking + enforcement.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/probe/sandbox_exec.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6254ace7c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

plan.Image,
)
args = append(args, verifiedApplicationArgvV1(command.Argv)...)
args = append(args, sandboxApplicationArgvV1(plan, command.Argv, true, []int{})...)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve DNS for public-only transient containers

When public: allow is combined with local: deny, applying the sandbox here breaks hostname-based public access for transient commands and shells on Docker hosts whose default bridge copies a private DNS resolver (for example, a corporate 10.x nameserver) into the container. This renderer supplies neither --network nor --dns, so unlike workloads on the Compose user-defined network, these containers do not reliably use Docker's 127.0.0.11 embedded resolver; the new output rules classify their resolver as local and drop every query. Attach transient containers to an embedded-DNS network or otherwise preserve DNS without granting general local access.

Useful? React with 👍 / 👎.

@omry

omry commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d7013d66c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +59 to +60
var applicationAmbiguousIPv6CIDRsV1 = []string{
"::ffff:0:0/96", "64:ff9b::/96", "64:ff9b:1::/48", "2001::/32", "2002::/16",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop classifying mapped IPv4 sockets only in the IPv6 chain

For connections to ::ffff:0:0/96, both Go's dialer and Linux's IPv6 socket implementation send an IPv4 packet, so this IPv6 nftables rule never sees the listed prefix. Consequently, ambiguous: allow still rejects mapped destinations when their actual IPv4 class is denied, while require-both permits mapped public destinations whenever public alone is allowed. The packet-family rules or the documented ambiguous classification need to account for the IPv4 form actually emitted.

Useful? React with 👍 / 👎.

Add portable public and local runtime-network controls that default to deny, compile into the locked runtime policy, and grant declared inbound ports only to the persistent workload shape. Classify translation and tunneling ranges conservatively, require both ordinary grants by default, and provide a documented temporary escape hatch for environments that explicitly need the ambiguous class.

Install IPv4 and IPv6 nftables policy through the trusted startup helper, retain declared-only inbound admission even when egress is unrestricted, then irreversibly drop setup capabilities and assume the final application identity for workloads, commands, shells, lifecycle actions, and private-environment execution. Preserve declared endpoint traffic, constrain Docker DNS, admit only established responses plus related ICMP network errors, and surface setup failures with their backend diagnostics.

Add unit and live Docker coverage for all policy combinations, public exceptions, ambiguous ranges, declared and undeclared inbound ports, root and non-root execution, DNS, endpoint persistence, and transient isolation. Run the live policy matrix in CI, document the security boundary and temporary escape hatch, update the deferred gateway backlog, and revise the security changelog fragment.
@omry

omry commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f0f7ede689

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/blueprint/model.go
Comment thread internal/deploy/runtime_policy.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved PR reviewed and approved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants